package org.example.lotterysystem.service.impl;
import org.example.lotterysystem.common.errorcode.ServiceErrorCodeConstants;
import org.example.lotterysystem.common.exception.ServiceExcetion;
import org.example.lotterysystem.common.utils.JacksonUtil;
import org.example.lotterysystem.common.utils.RedisUtil;
import org.example.lotterysystem.controller.param.DrawPrizeParam;
import org.example.lotterysystem.controller.param.ShowWinningRecordsParam;
import org.example.lotterysystem.dao.dataobject.*;
import org.example.lotterysystem.dao.mapper.*;
import org.example.lotterysystem.service.DrawPrizeService;
import org.example.lotterysystem.service.dto.WinningRecordDTO;
import org.example.lotterysystem.service.enums.ActivityPrizeStatusEnum;
import org.example.lotterysystem.service.enums.ActivityPrizeTiersEnum;
import org.example.lotterysystem.service.enums.ActivityStatusEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.example.lotterysystem.dao.dataobject.WinnerRecordDO;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

import static org.example.lotterysystem.common.config.DirectRabbitConfig.EXCHANGE_NAME;
import static org.example.lotterysystem.common.config.DirectRabbitConfig.ROUTING;

@Service
public class DrawPrizeServiceImpl implements DrawPrizeService {
    private static final Logger logger = LoggerFactory.getLogger(DrawPrizeServiceImpl.class);
    private final Long WINNING_RECORDS_TIMEOUT = 60 * 60 * 24 * 2L;
    private final String WINNING_RECORDS_PREFIX = "WINNING_RECORDS_";

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private ActivityMapper activityMapper;

    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private PrizeMapper prizeMapper;

    @Autowired
    private WinnerRecordMapper winnerRecordMapper;

    @Autowired
    private RedisUtil redisUtil;

    @Override
    public void drawPrize(DrawPrizeParam param) {
        Map<String,String> map = new HashMap<>();
        map.put("messageId",String.valueOf(UUID.randomUUID()));
        map.put("messageData", JacksonUtil.writeValueAsString(param));
        //发消息:交换机、绑定的key、消息体
        rabbitTemplate.convertAndSend(EXCHANGE_NAME,ROUTING,map);
        logger.info("mq消息发送成功：map={}",JacksonUtil.writeValueAsString(map));
    }

    /**
     * 校验抽奖请求信息
     * @param param
     * @return
     */
    @Override
    public Boolean checkDrawPrizeParam(DrawPrizeParam param) {

        ActivityDO activityDO = activityMapper.selectById(param.getActivityId());
        //奖品是否存在可以从activity_prize里面查，原因是保存activity做了本地事务，保证一致性
        ActivityPrizeDo activityPrizeDo = activityPrizeMapper.selectByAPId
                (param.getActivityId(),param.getPrizeId());
        //活动或奖品是否存在
        if(null == activityDO||null == activityPrizeDo){
//            throw new ServiceExcetion(ServiceErrorCodeConstants.ACTIVITY_OR_PRIZE_IS_EMPTY);
            logger.info("校验抽奖请求信息失败！失败原因：{}",ServiceErrorCodeConstants.ACTIVITY_OR_PRIZE_IS_EMPTY.getMsg());
            return false;
        }
        //活动是否有效
        if(activityDO.getStatus().equalsIgnoreCase(ActivityStatusEnum.COMPLETED.name())){
//            throw new ServiceExcetion(ServiceErrorCodeConstants.ACTIVITY_COMPLETED);
            logger.info("校验抽奖请求信息失败！失败原因：{}",ServiceErrorCodeConstants.ACTIVITY_COMPLETED.getMsg());
            return false;
        }
        //奖品是否有效
        if(activityPrizeDo.getStatus()
                .equalsIgnoreCase(ActivityPrizeStatusEnum.COMPLETED.name())){
//            throw new ServiceExcetion(ServiceErrorCodeConstants.ACTIVITY_PRIZE_COMPLETED);
            logger.info("校验抽奖请求信息失败！失败原因：{}",ServiceErrorCodeConstants.ACTIVITY_PRIZE_COMPLETED.getMsg());
            return false;
        }
        //中奖者人数是否和设置的奖品的数量一致
        if(activityPrizeDo.getPrizeAmount() != param.getWinnerList().size()){
//            throw new ServiceExcetion(ServiceErrorCodeConstants.WINNER_PRIZE_AMOUNT_COMPLETE);
            logger.info("校验抽奖请求信息失败！失败原因：{}",ServiceErrorCodeConstants.WINNER_PRIZE_AMOUNT_COMPLETE.getMsg());
            return false;
        }
        return true;
    }

    @Override
    public List<WinnerRecordDO> saveWinnerRecords(DrawPrizeParam param) {
        //查询相关信息：活动、人员、奖品、活动关联奖品
        ActivityDO activityDO = activityMapper.selectById(param.getActivityId());
        List<UserDO> userDOList = userMapper.batchSelectByIds(
                param.getWinnerList()
                        .stream()
                        .map(DrawPrizeParam.Winner::getUserId)
                        .collect(Collectors.toList())
        );
        PrizeDO prizeDO = prizeMapper.selectById(param.getPrizeId());
        ActivityPrizeDo activityPrizeDo =
                activityPrizeMapper.selectByAPId(param.getActivityId(), param.getPrizeId());

        //构造中奖者记录、保存
        List<WinnerRecordDO> winnerRecordDOList = userDOList.stream()
                .map(userDO -> {
                    WinnerRecordDO winnerRecordDO = new WinnerRecordDO();
                    winnerRecordDO.setActivityId(activityDO.getId());
                    winnerRecordDO.setActivityName(activityDO.getActivityName());
                    winnerRecordDO.setPrizeId(prizeDO.getId());
                    winnerRecordDO.setPrizeName(prizeDO.getName());
                    winnerRecordDO.setPrizeTier(activityPrizeDo.getPrizeTiers());
                    winnerRecordDO.setWinnerId(userDO.getId());
                    winnerRecordDO.setWinnerName(userDO.getUserName());
                    winnerRecordDO.setWinnerEmail(userDO.getEmail());
                    winnerRecordDO.setWinnerPhoneNumber(userDO.getPhoneNumber());
                    winnerRecordDO.setWinningTime(param.getWinningTime());
                    return winnerRecordDO;
                }).collect(Collectors.toList());
          winnerRecordMapper.batchInsert(winnerRecordDOList);
        //缓存中奖者记录
        //1、缓存奖品维度中奖记录(activityId_prizeId,winningRecordDoList(奖品维度的中奖名单))
        cacheWinningRecords(param.getActivityId()+"_"+ param.getPrizeId(),winnerRecordDOList,WINNING_RECORDS_TIMEOUT);
        //2、缓存活动维度中奖记录（WinningRecord_activityId_prizeId,winningRecordDoList（活动维度的中奖名单）)
        //当活动已完成再进行存放
        //（ACTIVITY_activityId）
        if(activityDO.getStatus()
                .equalsIgnoreCase(ActivityStatusEnum.COMPLETED.name())){
            //查询活动维度的全量中奖记录
           List<WinnerRecordDO> allList = winnerRecordMapper.selectByActivityId(param.getActivityId());
            cacheWinningRecords(String.valueOf(param.getActivityId()),allList,WINNING_RECORDS_TIMEOUT);
        }
    return winnerRecordDOList;
    }

    @Override
    public void deleteRecords(Long activityId, Long prizeId) {
        if(null == activityId || prizeId == null){
            logger.warn("要删除中奖记录相关的活动id或奖品id为空！");
            return;
        }
        //删除数据表
        winnerRecordMapper.deleteRecords(activityId,prizeId);

        //删除缓存（奖品维度、活动维度）
        if(null != prizeId){
            deleteWinningRecords(activityId+"_"+prizeId);
        }
        //无论是否传递prizeId，都需要删除中奖记录缓存
        //如果传递了prizeId，证明奖品未抽奖，此时必须要删除活动维度的抽奖记录
        //如果没有传递prizeId，就只删除活动维度的信息
        deleteWinningRecords(String.valueOf(activityId));
    }

    @Override
    public List<WinningRecordDTO> getRecords(ShowWinningRecordsParam param) {
        //查询redis:奖品、活动
        String key = null == param.getPrizeId() ?
                String.valueOf(param.getActivityId())
                : param.getActivityId()+"_"+ param.getPrizeId();
        List<WinnerRecordDO> winnerRecordDOList = getWinnerRecords(key);
        if(!CollectionUtils.isEmpty(winnerRecordDOList)){
            return convertToWinningRecordsDTOList(winnerRecordDOList);
        }
        //如果redis不存在，查库
         winnerRecordDOList =
                 winnerRecordMapper.selectByActivityIdOrPrizeId(param.getActivityId(),param.getPrizeId());
        //存放redis
        if(CollectionUtils.isEmpty(winnerRecordDOList)){
            logger.info("查询的中奖记录为空：{}",
                    JacksonUtil.writeValueAsString(param));
            return Arrays.asList();
        }
        cacheWinningRecords(key,winnerRecordDOList,WINNING_RECORDS_TIMEOUT);
        return convertToWinningRecordsDTOList(winnerRecordDOList);
    }

    private List<WinningRecordDTO> convertToWinningRecordsDTOList(List<WinnerRecordDO> winnerRecordDOList) {
        if(CollectionUtils.isEmpty(winnerRecordDOList)){
            return Arrays.asList();
        }
        return winnerRecordDOList
                .stream()
                .map(winnerRecordDO -> {
                    WinningRecordDTO winningRecordDTO = new WinningRecordDTO();
                    winningRecordDTO.setWinnerId(winnerRecordDO.getWinnerId());
                    winningRecordDTO.setWinnerName(winnerRecordDO.getWinnerName());
                    winningRecordDTO.setPrizeName(winnerRecordDO.getPrizeName());
                    winningRecordDTO.setPrizeTire(
                            ActivityPrizeTiersEnum.forname(winnerRecordDO.getPrizeTier()));
                    winningRecordDTO.setWinningTime(winnerRecordDO.getWinningTime());
                    return winningRecordDTO;
        }).collect(Collectors.toList());
    }

    /**
     * 从缓存中删除中奖记录
     * @param key
     */
    private void deleteWinningRecords(String key) {
        try{
            if(redisUtil.hasKey(WINNING_RECORDS_PREFIX+key)) {
                //存在再删除
                redisUtil.del(WINNING_RECORDS_PREFIX+key);
            }
        }catch (Exception e){
            logger.error("删除中奖记录缓存异常！key:{}",key);
        }

    }

    /**
     * 缓存中奖记录
     * @param key
     * @param winnerRecordDOList
     * @param time
     */
    private void cacheWinningRecords(String key,
                                     List<WinnerRecordDO> winnerRecordDOList,
                                     Long time) {
        String value = "";
        try{
            if(!StringUtils.hasText(key)
                    || CollectionUtils.isEmpty(winnerRecordDOList)){
                logger.warn("要缓存的内容为空！key:{},value:{}",WINNING_RECORDS_PREFIX+key,JacksonUtil.writeValueAsString(winnerRecordDOList));
                return;
            }
            value = JacksonUtil.writeValueAsString(winnerRecordDOList);
            redisUtil.set(WINNING_RECORDS_PREFIX+key,
                    value,
                    time);
        }catch (Exception e){
            logger.error("缓存中奖记录异常！key:{},value:{}",WINNING_RECORDS_PREFIX+key,value);
        }
    }

    /**
     * 从缓存中获取中奖记录
     * @param key
     * @return
     */
    private  List<WinnerRecordDO> getWinnerRecords(String key){
       try {
           if(!StringUtils.hasText(key)){
               logger.warn("要从缓存中获取的中奖记录为空");
               return Arrays.asList();
           }
           String str = redisUtil.get(WINNING_RECORDS_PREFIX + key);
           if (StringUtils.hasText(str)) {
               return Arrays.asList();
           }
           List<WinnerRecordDO> winnerRecordDOList = JacksonUtil.readListValue(str, WinnerRecordDO.class);
           return winnerRecordDOList;
       } catch (Exception e) {
           logger.error("从缓存获取中奖记录异常！key:{}",WINNING_RECORDS_PREFIX+key);
           return Arrays.asList();
       }
    }
}
